/*
 * @[H]:  Copyright (c) 2021 Phytium Information Technology, Inc. 
 * 
 *  SPDX-License-Identifier: Apache-2.0. 
 * 
 * @Date: 2021-08-03 22:42:42
 * @LastEditTime: 2021-08-16 15:13:28
 * @Description:  Description of file
 * @Modify History: 
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */
#include "f_pcie_hw.h"
#include "f_pcie_dma.h"
#include "f_pcie.h"
#include "ft_types.h"
#include "cache.h"
#include "kernel.h"

#include "ft_debug.h"
#define FPCIE_DMA_DEBUG_TAG "FPCIE_DMA"
#define FPCIE_DMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__)
#define FPCIE_DMA_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__)
#define FPCIE_DMA_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__)
#define FPCIE_DMA_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__)

ft_error_t FPcieDmaDescSet(uintptr axi_addr,
                           uintptr pcie_addr,
                           u32 length,
                           struct FPcieDmaDescriptor *desc,
                           struct FPcieDmaDescriptor *next_desc)
{
    /* 设置内存地址 */
    desc->axi_base_address = axi_addr;
    desc->axi_address_phase_controls = 0x00;

    /* 设置pcie空间地址 */
    desc->pcie_base_address = pcie_addr;
    desc->pcie_tlp_header_attributes = 0x01;

    /* 设置数据长度， 设置dma完成中断 */
    desc->transfer_control = length;

    desc->axi_bus_status = 0x00;
    desc->pcie_bus_status = 0x00;
    desc->channel_status = 0x00;

    if (next_desc != NULL)
    {
        /* 使能链表模式 */
        desc->transfer_control &= ~(BIT(24));
        desc->transfer_control |= BIT(29);

        /* 设置下一级链表地址 */
        desc->next_descriptor = (uintptr)next_desc;
    }
    else
    {
        desc->transfer_control |= BIT(24);
        desc->transfer_control &= ~(BIT(29));

        desc->next_descriptor = 0;
    }

    return 0;
}

void FPcieDmaRead(uintptr cintrol_address, struct FPcieDmaDescriptor *desc)
{
    /* The enable channel is interrupted */
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_INT_ENABLE_OFFSET, FPCIE_CTRL_DMA_INT_ENABLE_CH0_DONE_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH0_ERR_MASK);

    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH0_SP_L_OFFSET, (u32)((uintptr)desc & 0xffffffffU));
#ifdef __aarch64__
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH0_SP_H_OFFSET, (u32)(((uintptr)desc >> 32) & 0xffffffffU));
#else
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH0_SP_H_OFFSET, 0);
#endif

    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH0_CTRL_OFFSET, FPCIE_CTRL_DMA_CH0_CTRL_GO_MASK);
}

void FPcieDmaWrite(uintptr cintrol_address, struct FPcieDmaDescriptor *desc)
{
    /* The enable channel is interrupted */
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_INT_ENABLE_OFFSET, FPCIE_CTRL_DMA_INT_ENABLE_CH1_DONE_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH1_ERR_MASK);

    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH1_SP_L_OFFSET, (u32)((uintptr)desc & 0xffffffffU));
#ifdef __aarch64__
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH1_SP_H_OFFSET, (u32)(((uintptr)desc >> 32) & 0xffffffffU));
#else
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH1_SP_H_OFFSET, 0);
#endif
    FPCIE_WRITEREG(cintrol_address, FPCIE_REG_DMA_CH1_CTRL_OFFSET, FPCIE_CTRL_DMA_CH1_CTRL_GO_MASK | FPCIE_CTRL_DMA_CH1_CTRL_OBNOTIB_MASK);
}

ft_error_t FPcieDmaPollDone(struct FPcieDmaDescriptor *desc, u32 wait_cnt)
{
    FPCIE_DMA_DEBUG_I("desc axi_bus_status :[0x%02x]", desc->axi_bus_status);
    FPCIE_DMA_DEBUG_I("desc pcie_bus_status:[0x%02x]", desc->pcie_bus_status);
    FPCIE_DMA_DEBUG_I("desc channel_status :[0x%02x]", desc->channel_status);

    while (wait_cnt > 0)
    {
        if (desc->channel_status == 0x1)
        {
            FPCIE_DMA_DEBUG_I("dma channel transfer done ");
            return FT_SUCCESS;
        }
        wait_cnt--;
    }

    return FPCIE_ERR_TIMEOUT;
}
